#include "stdafx.h"
#include <wchar.h>
#include "Search.h"
#include "hex.h"
#include "globals.h"

static int cmp_nocase_byte(u8 *str)
{
	return tolower(*str);
}

static int cmp_nocase_wide(u8 *str)
{
	return towlower(*((wchar_t*)str));
}

static int cmp_hex(u8 *str)
{
	return *str;
}

tag_search compare[]=
{
	&cmp_nocase_byte,1,
	&cmp_nocase_wide,2,
	&cmp_hex,1
};

CSearch::CSearch(void)
{
	search_data=NULL;
	search_size=0;
	tag=NULL;
}

CSearch::~CSearch(void)
{
	Reset();
}

void CSearch::Reset()
{
	search_str.Empty();
	if(search_data)
	{
		delete[] search_data;
		search_data=NULL;
	}
	search_size=0;
	tag=NULL;
}

// returns converted hex sequence size if succeded or -1 for incorrect strings
int CSearch::SetSearchHex(GString hex)
{
	int len=hex.GetLength();

	// show "Hex string is not recognized." error and return TRUE
	if(len % 2 != 0) return -1;
	// show "Hex string is not recognized." error and return TRUE
	if(!is_hex((_tstring)hex)) return -2;

	Reset();

	// set search function
	tag=&compare[CT_HEX];
	// parse hex string
	u8 *hex_search=new u8[len/2];
	u8 *h=hex_search;
	for(int i=0; i<len; i+=2)
		*h++=char_hex(hex.Mid(i,2));
	// fix bug with incorrect NULL termination
	search_size=len/2;
	// reference
	search_hex=hex;
	search_data=(LPVOID)hex_search;

	return search_size;
}

int CSearch::SetSearchExpression(GString expression, bool match_case, int charmode, int binmode)
{
	// searching empty data, not good
	if(expression.IsEmpty()) return -1;

	Reset();

	u8* hex_data;
	search_str=expression;
	search_size=bytecode_translate(&hex_data,(TCHAR*)expression.GetBuffer(),expression.GetLength(),charmode,binmode);
	//if(search_data) delete[] search_data;
	search_data=hex_data;

	// set search function
	if(match_case) tag=&compare[CT_HEX];
	else
	{
		if(charmode==ENCODING_BYTE) tag=&compare[CT_NOCASE_U8];
		else tag=&compare[CT_NOCASE_U16];
	}

	return search_size;
}

void CSearch::CreateRelativeSearchString(LPCTSTR string)
{
	_tstring temp = (_tstring) string;
	sRelSearchString = temp;
	vector<relation> emp;
	vRelativeOffsets = emp;
	relation a;
	std::vector<int> caps;
	std::vector<int> nums;
	std::vector<int> lows;

	int i = 0;
	for(i = 0;i<(int)temp.length();i++)
	{
		if(temp[i] >= 'A' && temp[i] <= 'Z')
			caps.push_back(i);
		else if(temp[i] >= '1' && temp[i] <= '9')
			nums.push_back(i);
		else if(temp[i] >= 'a' && temp[i] <= 'z')
			lows.push_back(i);
	}
	for(i = 0;i<(int)caps.size()-1;i++)
	{
		a.relate = caps[i];
		a.offset = temp[caps[i+1]] - temp[caps[i]];
		a.op = "=";
		a.related = caps[i+1];
		vRelativeOffsets.push_back(a);
	}
	for(i = 0;i<(int)lows.size()-1;i++)
	{
		a.relate = lows[i];
		a.offset = temp[lows[i+1]] - temp[lows[i]];
		a.op = "=";
		a.related = lows[i+1];
		vRelativeOffsets.push_back(a);
	}
	for(i = 0;i<(int)nums.size()-1;i++)
	{
		a.relate = nums[i];
		a.offset = temp[nums[i+1]] - temp[nums[i]];
		a.op = "=";
		a.related = nums[i+1];
		vRelativeOffsets.push_back(a);
	}
	if(lows.size() > 0 && caps.size() > 0)
	{
		a.relate = caps[0];
		a.offset = temp[lows[0]] - 'a';
		a.op = "<";
		a.related = lows[0];
		vRelativeOffsets.push_back(a);
	}
	if(lows.size() > 0 && nums.size() > 0)
	{
		a.relate = nums[0];
		a.offset = temp[lows[0]] - 'a';
		a.op = "<";
		a.related = lows[0];
		vRelativeOffsets.push_back(a);
	}
	if(caps.size() > 0 && nums.size() > 0)
	{
		a.relate = nums[0];
		a.offset = temp[caps[0]] - 'A';
		a.op = "<";
		a.related = caps[0];
		vRelativeOffsets.push_back(a);
	}
	//caps = viEmpty;
	//lows = viEmpty;
	//caps = viEmpty;
}

// SEARCH
// ps=buffer to be searched, ls=length
// pb=search source, lb=length again
// mode=1: direction up, 0: direction down
// cmp=comparison method
int find_bytes (LPVOID ps, int ls, LPVOID pb, int lb, int mode, tag_search *tag)
{
	int start, inc, end, i;
	u8* s1=(u8*)ps;
	u8* s2=(u8*)pb;

	// direction=down
	if (mode == 1)
	{
		start = 0;
		inc = 1;
		end = ls - lb + 1;
	}
	// direction up
	else
	{
		start = ls - lb;
		inc = -1;
		end = 1;
	}

	for (; mode * start < end; start += inc)
	{
		for (i = start; i < start + lb; i+=tag->size)
			if (tag->func(&s1[i]) != tag->func(&s2[i - start]))
				break;

		if (i == start + lb)
			return start;
	}

	return -1;
}
